import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;

/**
 * JSServer - Jam Session Server
 * 
 * @author James Varvaro
 */
public class JSServer {
    private int port;
    private ServerSocket serverSocket = null;
    
    private ConcurrentLinkedQueue<BroadcastDrop> broadcasts;
    private boolean listening;
    
    private final AtomicLong useridCount = new AtomicLong(0);
    
    public JSServer(int setPort) {
        this.port = setPort;
        
        try {
            serverSocket = new ServerSocket(port);
        }
        catch (IOException ex) {
            System.err.println("Could not listen on port: " + port);
            System.exit(-1);
        }
        
        // Setup broadcast handling
        broadcasts = new ConcurrentLinkedQueue<BroadcastDrop>();
        (new BroadcastHandler(broadcasts)).start();
        
        // Turn on listening
        this.listening = true;
        
    }
    
    public boolean isListening() {
        return listening;
    }
    
    public void setListening(boolean state) {
        this.listening = state;
    }
    
    public void closeConn() throws IOException {
        serverSocket.close();
    }
    
    static public void main(String[] args) throws Exception {
        
        // Set port number
        int argPort = 0;
        
        if(args.length == 0) {
            // Initialize from jsserver.cfg
            BufferedReader cfgReader = new BufferedReader(new FileReader("jsserver.cfg"));
            String line = null;
            while((line = cfgReader.readLine()) != null) {
                // Remove leading and trailing whitespace
                line = line.trim();
                // line is not empty
                if(line.length() > 0) {
                    // line is not a comment
                    if(line.charAt(0) != '#') {
                        StringTokenizer lineTokens = new StringTokenizer(line);
                        // Valid amount of arguments
                        if(lineTokens.countTokens() == 2) {
                            if(lineTokens.nextToken().equals("LISTEN")) {
                                int tmpPort = 0;
                                try {
                                    tmpPort = Integer.parseInt(lineTokens.nextToken());
                                }
                                catch (NumberFormatException ex) {
                                    System.err.println("Port argument must be an integer");
                                    System.exit(-1);
                                }
                                if(tmpPort > 1023 && tmpPort <= 65535) {
                                    // assign port
                                    argPort = tmpPort;
                                }
                                else {
                                    System.err.println("Invalid port number. Must be in the range of 1024 - 65535");
                                    System.exit(-1);
                                }
                            }
                        }
                    }
                }
            }
            // Close config file
            cfgReader.close();
            
            // Check if port was set
            if(argPort == 0) {
                System.err.println("A port was not assigned is jsserver.cfg .");
                System.err.println("Either add a LISTEN port line to the cfg file or specify the port as a command line argument.");
                System.exit(-1);
            }
            else {
                System.out.println("JSServer listen port set to: " + argPort);
            }
        }
        else if(args.length == 1) {
            // Command line initialization
            try {
                argPort = Integer.parseInt(args[0]);
            }
            catch (NumberFormatException ex) {
                System.err.println("Port argument must be an integer");
                System.exit(-1);
            }
        }
        else {
            System.err.println("JSServer takes 1 argument: port");
            System.exit(-1);
        }
        // init server
        JSServer server = new JSServer(argPort);

        while(server.listening) {
            // Create broadcast.
            BroadcastDrop broadcast = new BroadcastDrop();
            // Accept connection from client
            new JSServerThread(server.serverSocket.accept(), broadcast, server.useridCount).start();
            // Register broadcast
            server.broadcasts.add(broadcast);
        }
        
        // Close connection
        try {
            server.closeConn();
        }
        catch (IOException ex) { 
            System.out.println("Could not close server socket.");
        }
        
    }
}
